package com.xlkj.ms.tools.aop.aspects;

import com.alibaba.fastjson.JSON;
import com.xlkj.ms.tools.aop.annotations.Print;
import com.xlkj.ms.tools.core.utils.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import java.lang.reflect.Method;

/**
 * 日志打印切面
 * @author zhuqifeng
 * @date 2021/11/10 14:07
 * @package com.xlkj.ms.tools.aop.aspects
 */
@Component
@Aspect
public class PrintAspect {

    private final static Logger logger = LoggerFactory.getLogger(PrintAspect.class);

    @Pointcut("@annotation(print)")
    private void pointCutMethod(Print print) {}

    @Around(value = "pointCutMethod(print)", argNames = "pjp,print")
    public <T> Object doAround(ProceedingJoinPoint pjp, Print print) throws Throwable {
        StopWatch stopWatch = null;
        if(print.totalMileTime()){
            stopWatch = new StopWatch();
            stopWatch.start();
        }
        Object object = null;
        //获取操作名，如果不传，那么取class.method作为操作名
        String operMsg = StringUtils.isBlank(print.opeMethod())
                ? pjp.getTarget().getClass().getCanonicalName() + "." + ((MethodSignature) pjp.getSignature()).getMethod().getName()
                : print.opeMethod();

        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        //获取返回类
        Class<?> returnClass = method.getReturnType();

        try {
            //打印请求报文
            if(logger.isDebugEnabled() || print.request()){
                //获取请求参数
                Object[] args = pjp.getArgs();
                logger.info("[{}]请求报文：{}",operMsg, args);
            }else{
                logger.info("进入[{}]请求",operMsg);
            }
            //执行方法块中的逻辑
            object = pjp.proceed();
            return object;
        } finally {
            if(null!=stopWatch && stopWatch.isRunning()){
                stopWatch.stop();
                long totalMillis = stopWatch.getTotalTimeMillis();
                printResponse(returnClass, operMsg ,print, object, totalMillis);
            }else{
                printTimeResponseWithout(returnClass, operMsg, print, object);
            }

        }
    }

    private void printResponse(Class<?> returnClass, String operMsg, Print print, Object object, long totalMillis){
        if(!isVoid(returnClass)){
            //debug或者允许打印的时候才打印
            if(logger.isDebugEnabled() || print.response()){
                logger.info("[{}][耗时：{}ms],返回报文:{}",operMsg,totalMillis, JSON.toJSONString(object));
            }else{
                //不允许打印返回报文的时候，只打印耗时
                logger.info("[{}][耗时：{}ms]",operMsg,totalMillis);
            }
        }else{
            //如果返回是void，那么会打印耗时
            logger.info("[{}][耗时：{}ms]",operMsg,totalMillis);
        }
    }

    private void printTimeResponseWithout(Class<?> returnClass, String operMsg, Print print, Object object){
        if(!isVoid(returnClass)){
            //debug或者允许打印的时候才打印
            if(logger.isDebugEnabled() || print.response()){
                logger.info("[{}],返回报文:{}",operMsg, JSON.toJSONString(object));
            }
        }
    }

    /**
     * 判断接口返回是不是void
     * @param clazz
     * @return
     */
    private boolean isVoid(Class<?> clazz){
        return "void".equalsIgnoreCase(clazz.getName());
    }
}